#include "../include/factory.h" // 工厂模式相关函数的头文件
#include "../include/ser_cli.h" // 服务器与客户端通信相关函数的头文件
#include "../include/sql.h"     // 数据库操作相关函数的头文件

// 主函数
// 参数:
// argc - 命令行参数个数
// argv - 命令行参数数组，argv[1]为配置文件路径
int main(int argc, char *argv[])
{
    // 检查命令行参数是否正确，要求至少两个参数
    ARGS_CHECK(argc, 2);

    // 打开配置文件，获取服务器配置
    FILE *fp = fopen(argv[1], "r+");
    ERROR_CHECK(fp, NULL, "fopen");

    int socket_fd, new_fd; // socket_fd为监听套接字，new_fd为新连接的套接字
    tcpInit(&socket_fd, fp); // 初始化TCP服务器，绑定端口并监听
    fclose(fp); // 关闭配置文件

    // 定义客户端和线程池相关数据结构
    client_t client[SOCKET_NUM]; // 客户端数组，存储每个客户端的状态
    process_data down_thread[DOWN_THREAD_NUM]; // 下载线程池
    process_data upload_thread[UP_THREAD_NUM]; // 上传线程池

    // 初始化线程池和客户端数组
    factoryInit(down_thread, upload_thread, client);

    // 启动线程池
    factoryStart(down_thread, upload_thread);

    // 创建epoll实例，用于事件监听
    int epfd = epoll_create(1);
    struct epoll_event evs[SOCKET_NUM]; // 存储epoll事件

    // 将监听套接字加入epoll监听
    epoll_func(epfd, socket_fd, EPOLL_CTL_ADD, EPOLLIN);

    // 将下载线程和上传线程的管道文件描述符加入epoll监听
    for (int i = 0; i < DOWN_THREAD_NUM; i++)
        epoll_func(epfd, down_thread[i].fd, EPOLL_CTL_ADD, EPOLLIN);
    for (int i = 0; i < UP_THREAD_NUM; i++)
        epoll_func(epfd, upload_thread[i].fd, EPOLL_CTL_ADD, EPOLLIN);

    // 定义轮盘调度相关变量
    int roud = 0; // 当前轮盘值
    int client_sum = 0; // 当前连接的客户端数量
    int ret; // 通用返回值变量

    struct sockaddr_in clien; // 客户端地址结构
    struct timeval start, end; // 用于记录时间的结构体
    Train_t train; // 数据传输结构体
    Zhuce login_msg; // 用户登录信息结构体
    QUR_msg qq_msg; // 查询请求结构体
    File_info file_info; // 文件信息结构体
    MQ_buf mqbuf; // 消息队列缓冲区
    char temp[50]; // 临时字符串缓冲区

    // 获取当前时间，作为轮盘调度的起始时间
    gettimeofday(&start, NULL);

    MYSQL *conn; // MySQL连接句柄
    sql_connect(&conn); // 连接数据库
    Llog(conn, "Server Start", "---", "---", "---"); // 记录服务器启动日志

    // 主循环，处理客户端连接和请求
    while (1)
    {
        // 等待epoll事件，超时时间为5000毫秒
        int ready_num = epoll_wait(epfd, evs, SOCKET_NUM, 5000);
        int nn_fd; // 当前处理的文件描述符

        gettimeofday(&end, NULL); // 获取当前时间

        // 检查轮盘超时，关闭超时的客户端连接
        if ((end.tv_sec - start.tv_sec) * 1000000 + end.tv_usec - start.tv_usec > 5000000)
        {
            roud = (roud + 1) % 5; // 更新轮盘值
            start = end; // 更新起始时间
            for (int j = 0; j < SOCKET_NUM; j++)
            {
                if (client[j].rotate == roud)
                {
                    epoll_func(epfd, j, EPOLL_CTL_DEL, EPOLLIN); // 从epoll中移除
                    close(j); // 关闭连接
                    client[j].state = 0; // 重置客户端状态
                    client[j].code = 0;
                    client[j].rotate = -1;
                    client_sum--; // 减少客户端计数
                    Llog(conn, "Timeout", client[j].name, "---", "Disconnect"); // 记录超时日志
                }
            }
        }

        // 遍历所有就绪的事件
        for (int i = 0; i < ready_num; i++)
        {
            nn_fd = evs[i].data.fd; // 获取当前事件的文件描述符

            // 处理新客户端连接
            if (evs[i].events == EPOLLIN && nn_fd == socket_fd)
            {
                ret = sizeof(clien);
                new_fd = accept(socket_fd, (struct sockaddr*)&clien, &ret); // 接受新连接
                if (client_sum >= MAX_CONNECT_NUM) // 如果连接数已达上限
                    close(new_fd); // 关闭新连接
                else
                {
                    client[new_fd].state = 1; // 设置客户端状态为已连接未登录
                    client_sum++; // 增加客户端计数
                    client[new_fd].code = 0;
                    client[new_fd].rotate = roud; // 设置轮盘值
                    epoll_func(epfd, new_fd, EPOLL_CTL_ADD, EPOLLIN); // 将新连接加入epoll监听
                    sprintf(client[new_fd].name, "ip:%s port:%d",
                            inet_ntoa(clien.sin_addr), ntohs(clien.sin_port)); // 记录客户端地址
                    Llog(conn, "Connect", "---", client[new_fd].name, "Request"); // 记录连接日志
                }
                continue;
            }

            // 下载文件完成
            for (int j = 0; j < DOWN_THREAD_NUM; j++)
            {
                if (evs[i].events == EPOLLIN && nn_fd == down_thread[j].fd)
                {
                    int k;
                    read(nn_fd, &ret, 4);
                    for (k = 0; k < DOWN_THREAD_NUM; k++)
                        if (down_thread[k].fd == nn_fd)
                            break;
                    down_thread[k].busy_num--;
                }
            }

            // 上传文件完成
            for (int j = 0; j < DOWN_THREAD_NUM; j++)
            {
                if (evs[i].events == EPOLLIN && nn_fd == upload_thread[j].fd)
                {
                    int k;
                    read(nn_fd, &ret, 4);
                    for (k = 0; k < UP_THREAD_NUM; k++)
                        if (upload_thread[k].fd == nn_fd)
                            break;
                    upload_thread[k].busy_num--;
                }
            }

            // 已连接客户端提出请求
            if (client[nn_fd].state > 0)
            {
                // 有任何请求都更新圆盘值，若上传或者下载则从新置为-1
                client[nn_fd].rotate = roud;
                ret = one_recv(nn_fd, &train);
                if (ret == -1)
                {
                    Llog(conn, "Client quit", client[new_fd].name, "---", "Sucess");
                    close(nn_fd);
                    client[nn_fd].state = 0;
                    client[nn_fd].code = 0;
                    client[nn_fd].rotate = -1;
                    client_sum--;
                    epoll_func(epfd, nn_fd, EPOLL_CTL_DEL, EPOLLIN);
                    continue;
                }
                switch (train.ctl_code)
                {
                case LOGIN_PRE:
                    Llog(conn, "Login request", train.buf, client[new_fd].name, "Sucess");
                    client[nn_fd].state = 1;
                    strcpy(client[nn_fd].name, train.buf);
                    // 判断这个id是否存在
                    ret = find_name(conn, client[nn_fd].name, NULL);
                    if (ret == 0)
                    {
                        train.Len = 0;
                        train.ctl_code = LOGIN_NO;
                        send(nn_fd, &train, 8, 0);
                    }
                    else
                    {
                        get_salt(train.buf);
                        train.Len = strlen(train.buf) + 1;
                        train.ctl_code = LOGIN_POK;
                        send(nn_fd, &train, 8 + train.Len, 0);
                    }
                    break;
                case LOGIN_Q: // 注册已经成功
                    memcpy(&login_msg, train.buf, train.Len);
                    add_user(conn, login_msg.name, login_msg.token, login_msg.passward);
                    Olog(conn, client[new_fd].name, "Login ", "---", "Sucess");
                    break;
                case REGISTER_PRE: // 登录请求
                    Llog(conn, "Register request", train.buf, client[new_fd].name, "Sucess");
                    client[nn_fd].state = 1;
                    strcpy(client[nn_fd].name, train.buf);
                    ret = find_name(conn, client[nn_fd].name, train.buf);
                    if (ret == -1)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else
                    {
                        train.Len = strlen(train.buf) + 1;
                        train.ctl_code = REGISTER_POK;
                    }
                    send(nn_fd, &train, 8 + train.Len, 0);
                    break;
                case REGISTER_Q:
                    memcpy(&login_msg, train.buf, train.Len);
                    ret = math_user(conn, login_msg.name,
                                    login_msg.passward, login_msg.token);
                    if (ret == -1)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                        Olog(conn, client[new_fd].name, "Register ", "---", "False");
                    }
                    else // 登录成功，state=2，name,round更新
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_OK;
                        strcpy(client[nn_fd].name, login_msg.name);
                        client[nn_fd].state = 2;
                        Olog(conn, client[new_fd].name, "Register ", "---", "Success");
                    }
                    send(nn_fd, &train, 8 + train.Len, 0);
                    break;
                case TOKEN_PLESE:
                    memcpy(&login_msg, train.buf, train.Len);
                    Llog(conn, "Token request", login_msg.name, client[new_fd].name, "Sucess");
                    strcpy(client[nn_fd].name, login_msg.name);
                    client[nn_fd].code = atoi(login_msg.passward);
                    ret = math_token(conn, login_msg.name, login_msg.token);
                    if (ret == 0)
                    {
                        client[nn_fd].state = 2;
                        Olog(conn, client[new_fd].name, "Token ", "---", "Success");
                    }
                    else
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                        send(nn_fd, &train, 8 + train.Len, 0);
                        Olog(conn, client[new_fd].name, "Token ", "---", "False");
                    }
                    break;
                case LS_Q:
                    if (client[nn_fd].state == 2)
                    {
                        Olog(conn, client[nn_fd].name, "Ls", "---", "Success");
                        ls_func(conn, client[nn_fd].name, client[nn_fd].code, train.buf);
                        train.ctl_code = LS_OK;
                        train.Len = strlen(train.buf) + 1;
                    }
                    else
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    send(nn_fd, &train, 8 + train.Len, 0);
                    break;
                case OPERATE_Q:
                    if (client[nn_fd].state == 2)
                    {
                        memcpy(&qq_msg, train.buf, train.Len);
                        strcpy(temp, qq_msg.buf);
                        Olog(conn, client[nn_fd].name, qq_msg.buf, qq_msg.buf1, "Request");
                        ret = operate_func(conn, &train, &qq_msg, client[nn_fd].name, &client[nn_fd].code);
                        if (ret == -1)
                        {
                            Olog(conn, client[nn_fd].name, temp, "---", "False");
                            train.Len = 0;
                            train.ctl_code = OPERATE_NO;
                        }
                        else
                            Olog(conn, client[nn_fd].name, temp, "---", "Success");
                    }
                    else
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    send(nn_fd, &train, 8 + train.Len, 0);
                    break;
                case DOWNLOAD_PRE:
                    strcpy(temp, train.buf);
                    if (client[nn_fd].state < 2)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else
                    {
                        strcpy(file_info.filename, train.buf);
                        ret = find_file_info(conn, &file_info, client[nn_fd].name, client[nn_fd].code);
                        if (ret == -1)
                        {
                            train.Len = 0;
                            train.ctl_code = OPERATE_NO;
                            Olog(conn, client[nn_fd].name, "Download", temp, "Request false");
                        }
                        else
                        {
                            memcpy(train.buf, &file_info, sizeof(File_info));
                            train.Len = sizeof(File_info);
                            train.ctl_code = DOWNLOAD_POK;
                            Olog(conn, client[nn_fd].name, "Download", temp, "Request success");
                        }
                        send(nn_fd, &train, 8 + train.Len, 0);
                    }
                    break;
                case DOWN_MORE_PRE:
                    strcpy(temp, train.buf);
                    if (client[nn_fd].state < 2)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else
                    {
                        strcpy(file_info.filename, train.buf);
                        ret = find_file_info(conn, &file_info, client[nn_fd].name, client[nn_fd].code);
                        if (ret == -1)
                        {
                            train.Len = 0;
                            train.ctl_code = OPERATE_NO;
                            Olog(conn, client[nn_fd].name, "MDownload", temp, "Request false");
                        }
                        else
                        {
                            bzero(&mqbuf, sizeof(MQ_buf));
                            memcpy(&mqbuf.file, &file_info, sizeof(File_info));
                            ret = get_mqbuf(conn, &mqbuf);
                            if (ret == 0)
                            {
                                Olog(conn, client[nn_fd].name, "MDownload", temp, "Request success");
                                train.Len = sizeof(MQ_buf);
                                train.ctl_code = DOWN_MORE_POK;
                                memcpy(train.buf, &mqbuf, train.Len);
                            }
                            else
                            {
                                train.Len = 0;
                                train.ctl_code = OPERATE_NO;
                                Olog(conn, client[nn_fd].name, "MDownload", temp, "Request false");
                            }
                        }
                        printf("ret = %d\n", ret);
                        send(nn_fd, &train, 8 + train.Len, 0);
                    }
                    break;
                case DOWNLOAD_Q:
                    if (client[nn_fd].state < 2)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else // 准备移交权限
                    {
                        ret = math_task(down_thread, DOWN_THREAD_NUM, AVG_CLIENT_NUM);
                        if (ret == -1)
                        {
                            train.Len = 0;
                            // 应该再加个控制码表示服务器繁忙
                            train.ctl_code = OPERATE_NO;
                            send(nn_fd, &train, 8 + train.Len, 0);
                            break;
                        }
                        // 任务转移
                        *temp = 0;
                        sprintf(temp, "%s download thread:%d", temp, ret);
                        Olog(conn, client[nn_fd].name, "Download move", temp, "Success");
                        write(down_thread[ret].fd, &nn_fd, 4);
                        down_thread[ret].busy_num++;
                        epoll_func(epfd, nn_fd, EPOLL_CTL_DEL, EPOLLIN);
                        client[nn_fd].state = 0;
                        client[nn_fd].code = 0;
                        client[nn_fd].rotate = -1;
                        client_sum--;
                        break;
                    }
                case UPLOAD_PRE:
                    strcpy(temp, train.buf);
                    if (client[nn_fd].state < 2)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else
                    {
                        // -1;失败;0可以上传;1上传成功
                        memcpy(&file_info, train.buf, train.Len);
                        ret = math_uoload(conn, &file_info, client[nn_fd].name, client[nn_fd].code);
                        if (ret == -1)
                        {
                            train.Len = 0;
                            train.ctl_code = OPERATE_NO;
                            Olog(conn, client[nn_fd].name, "Upload", temp, "Request false");
                        }
                        else if (ret == 1)
                        {
                            train.ctl_code = UPLOAD_OK;
                            Olog(conn, client[nn_fd].name, "Upload", temp, "Upload sucess");
                        }
                        else
                        {
                            train.ctl_code = UPLOAD_POK;
                            Olog(conn, client[nn_fd].name, "Upload", temp, "Request sucess");
                        }
                        send(nn_fd, &train, 8 + train.Len, 0);
                    }
                    break;
                case UPLOAD_Q:
                    if (client[nn_fd].state < 2)
                    {
                        train.Len = 0;
                        train.ctl_code = REGISTER_NO;
                    }
                    else // 准备移交权限
                    {
                        ret = math_task(upload_thread, UP_THREAD_NUM, UP_TASK_NUM - 1);
                        if (ret == -1)
                        {
                            train.Len = 0;
                            // 应该再加个控制码表示服务器繁忙
                            train.ctl_code = OPERATE_NO;
                            send(nn_fd, &train, 8 + train.Len, 0);
                            break;
                        }
                        *temp = 0;
                        sprintf(temp, "%s upload thread:%d", temp, ret);
                        Olog(conn, client[nn_fd].name, "Upload move", temp, "Success");
                        // 任务转移
                        write(upload_thread[ret].fd, &nn_fd, 4);
                        upload_thread[ret].busy_num++;
                        epoll_func(epfd, nn_fd, EPOLL_CTL_DEL, EPOLLIN);
                        client[nn_fd].state = 0;
                        client[nn_fd].code = 0;
                        client[nn_fd].rotate = -1;
                        client_sum--;
                        break;
                    }
                }
            }
        }
    }

    return 0; // 程序正常退出
}
